package kz.edu.sdu.atm.server;

import java.io.IOException;
import java.io.PrintWriter;
import java.math.BigDecimal;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Scanner;

import kz.edu.sdu.atm.db.IGenericDbAdapter;
import kz.edu.sdu.atm.models.UserDTO;
import kz.edu.sdu.atm.util.Logger;

public class Main {
	static Logger log = new Logger(Main.class);
	static ServerSocket serverSocket;
	static Map<String, Socket> clientMap = new HashMap<String, Socket>();
	static Thread clientListener;
	static IServer server;
	static boolean done = false;
	static String bindAddress = "";
	static int listenPort = 12345;

	public static void main(String [] args){
		server = new Server();
		if (args.length == 4 && args[0].equals("-c")) {
			IGenericDbAdapter storeAdapter = server.getAdapter();
			UserDTO u = new UserDTO();
			u.setUsername(args[1]);
			u.setPassword(args[2]);
			u.setMoney(new BigDecimal(args[3]));
			storeAdapter.saveUser(u);
			storeAdapter.destroy();
		} else {
			if (args.length == 1) {
				listenPort = Integer.parseInt(args[0]);
			}
			try {
				serverSocket = new ServerSocket(listenPort);
				clientListener = new Thread(new ClientListener());
				clientListener.start();
				while (!done) {
					int q = System.in.read();
					if ((char) q == 'q') {
						log.debug("CLOSING SERVER");
						done = true;
					}
					log.debug("read");
				}
				serverSocket.close();
				log.debug("WAIT while CLIENT LISTENER TERMINTATES");
				while (!clientListener.getState().equals(
						Thread.State.TERMINATED)) {
					Thread.sleep(100);
				}
				server.destroy();
				log.debug("QUIT");
			} catch (Exception e) {
				log.error(e.toString());
			}
		}
	}

	public static class ClientListener implements Runnable {
		Logger log = new Logger(ClientListener.class);
		List<Thread> serveList = new ArrayList<Thread>();

		@Override
		public void run() {
			while (!done) {
				try {
					for (Thread t : serveList) {
						if (t.getState().equals(Thread.State.TERMINATED)) {
							serveList.remove(t);
						}
					}
					log.debug("listen");
					Socket tmp = serverSocket.accept();
					String key = tmp.getRemoteSocketAddress().toString();
					clientMap.put(key, tmp);
					Thread tmpServe = new Thread(new Serve(tmp));
					serveList.add(tmpServe);
					tmpServe.start();
					log.debug(String.format("new client: %s", key));
				} catch (Exception e) {
					log.error("connection error");
					log.error(e.toString());
				}
			}
			if (serveList.size() != 0)
				log.debug("WAIT while SERVE Threads TERMINTATES");
			while (serveList.size() != 0) {
				for (Thread t : serveList) {
					if (t.getState().equals(Thread.State.TERMINATED)) {
						serveList.remove(t);
					}
				}
			}
		}

	}

	public static class Serve implements Runnable {
		Logger log;
		Socket client;

		public Serve() {
			log = new Logger(ClientListener.class);
		}

		public Serve(Socket s) {
			client = s;
			log = new Logger(String.format("%s - %s", ClientListener.class
					.getName(), client.getInetAddress().getHostName()));
		}

		@Override
		public void run() {
			try{
				Scanner in = new Scanner(client.getInputStream());
				PrintWriter out = new PrintWriter(client.getOutputStream());
				ComProtocol p = new ComProtocol(in, out, server);
				p.speak();
				in.close();
				out.close();
				client.close();
			}catch(IOException e){
				log.debug(e.toString());
			} finally {
			}
		}

	}

}
